#pragma once
#include"primitives.cpp"
#include"cell.cpp"

class gate
{
public:
    bool is_dff = 0;
    bool is_sensitive_dff = 0;
    bool in_event = 0;
    short top_level = -1;
    unsigned char sizeOfsim = 0;
    unsigned char sizeOfwire = 0;
    char* out_1 = nullptr;
    char* out_2 = nullptr;

    std::vector<std::pair<void(*)(std::vector<char*>&),std::vector<char*>>> sim;

    std::vector<std::tuple<char,char>> wire; //input & internal wire & output : current response, old response -> <value, value>
 
    std::vector<std::vector<std::tuple<char*,gate*,bool>>> output_to_bits; 

    std::string name="";

    explicit gate(const cell&);
    inline int simulate() noexcept;
    void initial_gate(int&, int&,std::vector<std::vector<std::pair<char,char>>>&) noexcept;
};

gate::gate(const cell& c) : sim(c.sim),name(c.name),is_dff(c.is_dff),sizeOfsim(c.sim.size()),sizeOfwire(c.wire.size())
{
    for(auto& [_,v1,v2] : c.wire)
        wire.emplace_back(v1,v2);
}

inline int gate::simulate() noexcept
{
    char old_output_1 = *out_1;
    char old_output_2 = *out_2;

    for(int i=0;i<sizeOfsim;++i)       
        sim[i].first(sim[i].second);

    int changed = 0;

    changed |= (*out_1 != old_output_1);
        
    if(out_1 != out_2) 
    {
        changed = changed<<1;
        changed |= (*out_2 != old_output_2);        
    }
    return changed;
}

void gate::initial_gate(int& out1, int& out2,std::vector<std::vector<std::pair<char,char>>>& uninitial_para) noexcept
{
    out_1 = &(std::get<0>(wire[out1]));
    out_2 = &(std::get<0>(wire[out2]));

    for(int i=0;i<sizeOfsim;++i)
    {
        auto& sim_para = sim[i].second;
        sim_para.emplace_back(&(std::get<0>(wire[uninitial_para[i][0].first])));
        for(int j=1,sz2=uninitial_para[i].size();j<sz2;++j)
        {
            if(uninitial_para[i][j].second==1)
                sim_para.emplace_back(&(std::get<0>(wire[uninitial_para[i][j].first])));
            else
                sim_para.emplace_back(&(std::get<1>(wire[uninitial_para[i][j].first])));
        }

        if(sim[i].first==pri::DFF_PRI)
        {
            sim_para.emplace_back(&(std::get<1>(wire[uninitial_para[i][3].first])));
            sim_para.emplace_back(&(std::get<1>(wire[uninitial_para[i][0].first])));
            sim_para.emplace_back(&(std::get<1>(wire[uninitial_para[i][1].first])));
            sim_para.emplace_back(&(std::get<1>(wire[uninitial_para[i][2].first])));
        }
    }
};
